package cn.acmenms.manager.tomcat;

import cn.acmenms.utils.StringTools;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.BasicHttpContext;
import org.quartz.utils.PoolingConnectionProvider;
import org.springframework.web.util.DefaultUriBuilderFactory;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

/**
 * @ClassName: TomcatManager
 * @Description: 实现用户远程请求Tomcat ManagerServlet，方便远程部署war包
 * @author: 神奇的鸭鸭
 * @Date: 2017/12/7 8:50
 */
public class TomcatManager {

    private String username;
    private URL url;
    private String password;
    private CloseableHttpClient httpClient;
    private HttpClientContext context;

    /**
     * 创建tomcat Manager
     * @param url
     * @param username
     * @param password
     */
    public TomcatManager(URL url, String username, String password) throws IOException {
        this.url = url;
        this.username = username;
        this.password = password;
        this.httpClient = HttpClients.createDefault();
        this.context = HttpClientContext.create();

        if (StringTools.isNotEmpty(username) && StringTools.isNotEmpty(password)) {
            Credentials creds = new UsernamePasswordCredentials(username, password);
            String host = url.getHost();
            int port = url.getPort() > -1 ? url.getPort() : AuthScope.ANY_PORT;

            AuthCache authCache = new BasicAuthCache();
            BasicScheme basicAuth = new BasicScheme();
            // Holds all of the variables needed to describe an HTTP connection to a host. This includes remote host name, port and scheme.
            HttpHost targetHost = new HttpHost(url.getHost(), url.getPort(), url.getProtocol());
            authCache.put(targetHost, basicAuth);
            context.setCredentialsProvider(new BasicCredentialsProvider());
            context.getCredentialsProvider().setCredentials(new AuthScope(host, port), creds);
            context.setAttribute(ClientContext.AUTH_CACHE, authCache);
        }
    }

    /**
     * 真正发送http请求的地方，利于拓展其他请求
     *
     * @param path
     * @return tomcatManagerResponse
     */
    private TomcatManagerResponse invoke(String path) throws IOException {
        HttpRequestBase httpRequestBase = new HttpGet(url + path);
        HttpResponse response = httpClient.execute(httpRequestBase, context);

        int statusCode = response.getStatusLine().getStatusCode();
        switch (statusCode) {
            case HttpStatus.SC_OK: // 200
            case HttpStatus.SC_CREATED: // 201
            case HttpStatus.SC_ACCEPTED: // 202
                break;
            case HttpStatus.SC_MOVED_PERMANENTLY: // 301
            case HttpStatus.SC_MOVED_TEMPORARILY: // 302
            case HttpStatus.SC_SEE_OTHER: // 303
                String relocateUrl = calculateRelocatedUrl(response);
                this.url = new URL(relocateUrl);
                return invoke(path);
        }
        return new TomcatManagerResponse().setStatusCode(response.getStatusLine().getStatusCode())
                .setReasonPhrase(response.getStatusLine().getReasonPhrase())
                .setHttpResponseBody(IOUtils.toString(response.getEntity().getContent()));
    }

    /**
     * 请求失败，重新安置路径
     *
     * @param response
     * @return 重置路径
     */
    private String calculateRelocatedUrl(HttpResponse response) {
        Header locationHeader = response.getFirstHeader("Location");
        String locationField = locationHeader.getValue();
        // is it a relative Location or a full ?
        return locationField.startsWith("http") ? locationField : url.toString() + '/' + locationField;
    }

    /**
     * 向tomcat部署war包，此方法只拼接请求路径
     *
     * @param war    要发送的war包
     * @param update 是否覆盖
     * @return tomcatManagerResponse
     * @throws Exception
     */
    public TomcatManagerResponse deploy(File war, boolean update) throws Exception {
        StringBuilder buffer = new StringBuilder("/deploy");
        buffer.append("?path=/").append(URLEncoder.encode(war.getName(), "UTF-8"));
        if (war != null) {
            buffer.append("&war=").append(URLEncoder.encode(war.toString(), "UTF-8"));
        }
        if (update) {
            buffer.append("&update=true");
        }
        return invoke(buffer.toString());
    }
}